﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Security.Application;
using HtmlAgilityPack;

namespace B10.Helpers
{
    public class AntiXssHtmlHelper
    {
        readonly static string _videoTag = "<param name=\"allowfullscreen\" value=\"true\" /><param name=\"allowscriptaccess\" value=\"always\" /><param name=\"movie\" value=\"{0}\" /><embed src=\"{0}\" type=\"application/x-shockwave-flash\" allowfullscreen=\"true\" allowscriptaccess=\"always\" width=\"{1}\" height=\"{2}\"></embed>";

        public static string GetSafeHtml(string html)
        {
            try
            {
                if (string.IsNullOrEmpty(html))
                    return String.Empty;
                string newHtml;
                Dictionary<string, VideoTag> videos = GetVideos(html, out newHtml);
                if (string.IsNullOrEmpty(newHtml))
                    return String.Empty;

                newHtml = AntiXss.GetSafeHtmlFragment(newHtml);
                newHtml = ReplaceVideos(newHtml, videos);
                newHtml = ReplaceLinks(newHtml);

                return newHtml;
            }
            catch
            {
                return String.Empty;
            }
        }



        #region private methods

        private static string ReplaceLinks(string html)
        {
            if (string.IsNullOrEmpty(html))
                return String.Empty;
            HtmlDocument doc = new HtmlDocument();
            doc.OptionWriteEmptyNodes = true;
            doc.LoadHtml(html);
            if (doc.DocumentNode == null)
                return String.Empty;
            HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a");
            if (nodes == null || nodes.Count == 0)
                return html;
            foreach (HtmlNode item in nodes)
            {
                HtmlAttribute attr = null;
                if (item.HasAttributes)
                    attr = item.Attributes["id"];
                if (attr == null)
                {
                    attr = doc.CreateAttribute("rel", "nofollow");
                    item.Attributes.Append(attr);
                }
                else if (string.IsNullOrEmpty(attr.Value))
                    attr.Value = "nofollow";
                else if (!attr.Value.Contains("nofollow"))
                    attr.Value += ",nofollow";

            }
            return doc.DocumentNode.OuterHtml;
        }

        private static string ReplaceVideos(string html, Dictionary<string, VideoTag> videos)
        {
            try
            {
                if (string.IsNullOrEmpty(html))
                    return String.Empty;
                HtmlDocument doc = new HtmlDocument();
                doc.OptionWriteEmptyNodes = true;
                doc.LoadHtml(html);
                if (doc.DocumentNode == null)
                    return String.Empty;
                HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//div[@title='pseudo']");
                if (nodes == null || nodes.Count == 0)
                    return html;
                foreach (HtmlNode item in nodes)
                {
                    if (!item.HasAttributes)
                        continue;
                    HtmlAttribute attr = item.Attributes["id"];
                    if (attr == null || String.IsNullOrEmpty(attr.Value))
                        continue;
                    if (!videos.ContainsKey(attr.Value))
                        continue;

                    var video = videos[attr.Value];

                    item.Name = "object";
                    item.RemoveAllChildren();
                    item.Attributes.RemoveAll();
                    item.SetAttributeValue("width", video.Width);
                    item.SetAttributeValue("height", video.Height);
                    item.SetAttributeValue("classid", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000");
                    item.SetAttributeValue("codebase",
                        "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0");
                    item.InnerHtml = String.Format(_videoTag, video.Url, video.Width, video.Height);

                }
                return doc.DocumentNode.OuterHtml;
            }
            catch
            {
                throw;
            }
        }

        private static Dictionary<string, VideoTag> GetVideos(string html, out string newHtml)
        {
            newHtml = html;
            try
            {
                if (string.IsNullOrEmpty(html))
                    return null;
                HtmlDocument doc = new HtmlDocument();
                doc.OptionWriteEmptyNodes = true;
                doc.LoadHtml(html);
                if (doc.DocumentNode == null)
                    return null;
                HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//object");
                if (nodes == null || nodes.Count == 0)
                    return null;
                var res = GetVodeos(nodes);
                newHtml = doc.DocumentNode.OuterHtml;
                return res;
            }
            catch
            { }
            return null;
        }

        private static Dictionary<string, VideoTag> GetVodeos(HtmlNodeCollection nodes)
        {
            Dictionary<string, VideoTag> videos = new Dictionary<string, VideoTag>();
            foreach (HtmlNode item in nodes)
            {
                if (!item.HasChildNodes)
                    continue;
                HtmlNode n = item.SelectSingleNode("embed");
                if (n == null || !n.HasAttributes)
                    continue;
                HtmlAttribute attr = n.Attributes["src"];
                if (attr == null)
                    continue;
                if (!IsValidVideoSource(attr.Value))
                    continue;
                VideoTag video = new VideoTag();
                video.Url = HttpUtility.UrlPathEncode(attr.Value);

                attr = n.Attributes["width"];
                if (attr != null && !String.IsNullOrEmpty(attr.Value))
                    video.Width = AntiXss.HtmlAttributeEncode(attr.Value);
                attr = n.Attributes["height"];
                if (attr != null && !String.IsNullOrEmpty(attr.Value))
                    video.Height = AntiXss.HtmlAttributeEncode(attr.Value);

                string guid = Guid.NewGuid().ToString("N");

                item.Name = "div";
                item.RemoveAllChildren();
                item.Attributes.RemoveAll();
                item.SetAttributeValue("title", "pseudo");
                item.SetAttributeValue("id", guid);


                videos.Add("x_" + guid, video);
            }
            return videos;
        }

        private static bool IsValidVideoSource(string url)
        {
            if (!Uri.IsWellFormedUriString(url, UriKind.Absolute))
                return false;
            Uri uri = new Uri(url);
            if (uri == null || String.IsNullOrEmpty(uri.DnsSafeHost))
                return false;
            string host = uri.DnsSafeHost.TrimStart('w', '.').Trim();
            return host == "youtube.com" || host == "vimeo.com" ||
                host == "metacafe.com" || host == "video.google.com";
        }

        #endregion
    }

    class VideoTag
    {
        public VideoTag()
        {
            Url = Width = Height = String.Empty;
        }

        public string Url { get; set; }
        public string Width { get; set; }
        public string Height { get; set; }
    }
}
